`timescale 1ns/1ps
`include "../define.vh"

module mult_top_tb;

// 时钟和复位信号
reg clk;
reg sys_rst_n;
reg en;
reg [1:0] data_type;
reg [`DATA_SIZE-1:0] data_in;
reg [`DATA_SIZE-1:0] weight_in;
wire [`DATA_SIZE-1:0] float_o;
wire mult_out_valid;

// 定义输入-预期结果队列
typedef struct {
    logic [15:0] a;        // 输入值a
    logic [15:0] b;        // 输入值b
    logic [15:0] expected; // 预期结果
    string       test_name;
} test_item;
test_item test_queue[$];
int test_case_num = 0;

// 时钟生成（200MHz）
always #2.5 clk = ~clk;

// 实例化被测模块
Mult_top uut (
    .clk(clk),
    .sys_rst_n(sys_rst_n),
    .en(en),
    .data_type(data_type),
    .data_in(data_in),
    .weight_in(weight_in),
    .float_o(float_o),
    .mult_out_valid(mult_out_valid)
);

// 文件读取初始化
initial begin
    int fd;
    string line;
    logic [15:0] a, b, expected;  // 临时变量读取文件
    
    // 打开测试用例文件
    fd = $fopen("D:/FPGA/TPU/TPU/fp16_mult_cases.txt", "r");
    if (!fd) begin
        $display("Error: Cannot open test case file!");
        $finish;
    end
    
    // 读取并解析测试用例
    while (!$feof(fd)) begin
        void'($fgets(line, fd));
        if (line.substr(0,1) == "//") continue; // 跳过注释
        
        if ($sscanf(line, "%h %h %h", a, b, expected) == 3) begin
            test_case_num++;
            test_queue.push_back('{
                a: a, 
                b: b, 
                expected: expected, 
                test_name: $sformatf("Case%0d: A=%h B=%h", test_case_num, a, b)
            });
        end
    end
    $fclose(fd);
end

// 主测试流程
initial begin
    // 初始化
    clk = 0;
    sys_rst_n = 0;
    en = 0;
    data_type = 2'b00;
    data_in = 0;
    weight_in = 0;

    // 同步复位
    repeat(2) @(posedge clk);
    sys_rst_n = 1;
    data_type = `FP16;
    @(posedge clk);
    en = 1;


    //--------------------------
    // 连续输入测试用例（背靠背）
    //--------------------------
    fork
        // 输入线程：在en拉高的同一周期驱动首个数据
        begin
            // 立即驱动第一个数据（无需等待时钟边沿）
            data_in  = {16'h0, test_queue[0].a};
            weight_in= {16'h0, test_queue[0].b};
            $display("[%t] Sent: %s", $time, test_queue[0].test_name);
            
            // 后续数据每个周期更新一次
            for (int i=1; i<test_case_num; i++) begin
                @(posedge clk);
                data_in  = {16'h0, test_queue[i].a};
                weight_in= {16'h0, test_queue[i].b};
                $display("[%t] Sent: %s", $time, test_queue[i].test_name);
            end
        end

        // 校验线程：精确对齐5周期延迟
        begin
            automatic int delay_counter = 0;
            automatic int result_index = 0;
            forever begin
                @(posedge clk);
                if (mult_out_valid) begin
                    // 从首个数据输入开始计数，5周期后校验
                    if (result_index < test_case_num) begin
                        check_result(
                            test_queue[result_index].expected,
                            test_queue[result_index].test_name
                        );
                        result_index++;
                    end
                end
            end
        end
    join
    
    #100;
    $display("All %0d test cases completed!", test_case_num);
    $finish;
end

// 校验任务（精确对齐延迟）
task check_result;
    input [15:0] expected;
    input string test_name;
    begin
        // 直接校验（已通过多周期等待对齐）
        if (float_o[15:0] !== expected) begin
            $error("[%t] FAIL: %s", $time, test_name);
            $display("  Expected: %h", expected);
            $display("  Actual:   %h", float_o[15:0]);
        end else begin
            $display("[%t] PASS: %s", $time, test_name);
        end
    end
endtask

endmodule